package com.kevin.util;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Date;

import static com.kevin.util.DateTimeUtil.DateTimePattern.*;

public class DateTimeUtil {

    private static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER = LINE.formatter;
    private static final DateTimeFormatter DEFAULT_DATE_FORMATTER = DATE_LINE.formatter;

    private DateTimeUtil() {}

    public static String getCurrentDate() {
        return DEFAULT_DATE_FORMATTER.format(LocalDate.now());
    }

    public static String getCurrentDate(DateTimePattern pattern) {
        return pattern.formatter.format(LocalDate.now());
    }

    public static void main(String[] args) {
//        LocalDateTime localDateTime = string2DateTime("2022-08-06 10:31:31.227");
//        System.out.println(dateTime2String(localDateTime));
//        System.out.println(getMsec(localDateTime));
//        System.out.println(local2UTCLong(localDateTime));
//
//        LocalDateTime localDateTime1 = transform(localDateTime, ZoneId.of("+0"));
//        System.out.println(dateTime2String(localDateTime1));
//        System.out.println(getMsec(localDateTime1));
//        System.out.println(local2UTCLong(localDateTime1));

//        LocalDateTime localDateTime = string2DateTime("2022-08-04 00:00:00");
//        System.out.println(getMsec(localDateTime));

        LocalTime localTime = LocalTime.of(14, 55);
        System.out.println();
    }

    public static Date toDate(LocalDateTime time) {
        return new Date(getMsec(time));
    }

    public static LocalDateTime fromDate(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    public static LocalDateTime local2UTC(LocalDateTime time) {
        ZonedDateTime local = ZonedDateTime.of(time, ZoneId.systemDefault());
        return transform(local, ZoneId.of("Z")).toLocalDateTime();
    }

    public static long local2UTCLong(LocalDateTime time) {
        ZonedDateTime local = ZonedDateTime.of(time, ZoneId.systemDefault());
        return transform(local, ZoneId.of("+0")).toInstant().toEpochMilli();
    }

    public static ZonedDateTime transform(ZonedDateTime zonedDateTime, ZoneId zone){
        return zonedDateTime.withZoneSameInstant(zone);
    }

    public static LocalDateTime transform(LocalDateTime time, ZoneId zone) {
        ZonedDateTime zonedDateTime = ZonedDateTime.of(time, ZoneId.systemDefault());
        ZonedDateTime transDateTime = zonedDateTime.withZoneSameInstant(zone);
        return transDateTime.toLocalDateTime();
    }

    public static LocalDateTime atStartOfDay(LocalDateTime time) {
        return LocalDateTime.of(time.toLocalDate(), LocalTime.MIN);
    }

    public static LocalDateTime atEndOfDay(LocalDateTime time) {
        return LocalDateTime.of(time.toLocalDate(), LocalTime.MAX);
    }

    public static String getCurrentTime() {
        return DateTimePattern.TIME_NONE_LINE.formatter.format(LocalTime.now());
    }

    public static String getCurrentDateTime() {
        return DEFAULT_DATETIME_FORMATTER.format(LocalDateTime.now());
    }

    public static String getCurrentDateTime(DateTimePattern pattern) {
        return pattern.formatter.format(LocalDateTime.now());
    }

    public static long getCurrentMsec() {
        return getMsec(LocalDateTime.now());
    }

    public static long getMsec(LocalDateTime time) {
        Instant instant = time.atZone(ZoneId.systemDefault()).toInstant();
        return instant.toEpochMilli();
    }

    public static LocalDateTime msec2DateTime(long epochMilli) {
        Instant instant = Instant.ofEpochMilli(epochMilli);
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    public static ZonedDateTime msec2ZonedDateTime(long epochMilli) {
        Instant instant = Instant.ofEpochMilli(epochMilli);
        return ZonedDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    public static LocalDateTime string2DateTime(String timeStr) {
        return LocalDateTime.parse(timeStr, DEFAULT_DATETIME_FORMATTER);
    }

    public static LocalDateTime string2DateTime(String timeStr, DateTimePattern pattern) {
        return LocalDateTime.parse(timeStr, pattern.formatter);
    }

    public static String dateTime2String(LocalDateTime time) {
        return DEFAULT_DATETIME_FORMATTER.format(time);
    }

    public static String dateTime2String(LocalDateTime time, DateTimePattern pattern) {
        return pattern.formatter.format(time);
    }

    public static LocalDate string2Date(String dateStr) {
        return LocalDate.parse(dateStr, DEFAULT_DATE_FORMATTER);
    }

    public static LocalDate string2Date(String dateStr, DateTimePattern pattern) {
        return LocalDate.parse(dateStr, pattern.formatter);
    }

    public static String date2String(LocalDate date) {
        return DEFAULT_DATE_FORMATTER.format(date);
    }

    public static String date2String(LocalDate date, DateTimePattern pattern) {
        return pattern.formatter.format(date);
    }

    public static LocalDateTime atStartOfDay(LocalDate date) {
        return LocalDateTime.of(date, LocalTime.MIN);
    }

    public static LocalDateTime atEndOfDay(LocalDate date) {
        return LocalDateTime.of(date, LocalTime.MAX);
    }

    public enum DateTimePattern {

        LINE("yyyy-MM-dd HH:mm:ss"),
        NONE_LINE("yyyyMMdd HH:mm:ss"),
        NONE_SEPARATOR("yyyyMMddHHmmss"),

        MILSEC_LINE("yyyy-MM-dd HH:mm:ss.SSS"),
        MILSEC_NONE_LINE("yyyyMMdd HH:mm:ss.SSS"),
        MILSEC_NONE_SEPARATOR("yyyyMMddHHmmssSSS"),

        DATE_LINE("yyyy-MM-dd"),
        DATE_NONE_LINE("yyyyMMdd"),

        TIME_NONE_LINE("HHmmss");

        private DateTimeFormatter formatter;

        DateTimePattern(String pattern) {
            formatter = DateTimeFormatter.ofPattern(pattern);
        }
    }
}